package com.own.component.elastic.util;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

/**
 * JavaTimeSerializer
 *
 * @author chenxueli
 * @date 2022-09-03 21:57:00
 */
public class JavaTimeSerializer {

    public static final String DATE_TIME_FORMATTER = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_FORMATTER = "yyyy-MM-dd";
    public static final String TIME_FORMATTER = "HH:mm:ss";
    public static final String ZONED_DATE_TIME_FORMATTER = "yyyy-MM-dd'T'HH:mm:ssSSSZ";
    public static final String OFFSET_DATE_TIME_FORMATTER = "yyyy-MM-dd'T'HH:mm:ssSSSZ";

    public static final SimpleModule MODULE = new SimpleModule();

    static {
        MODULE.addSerializer(LocalDate.class, new CustomLocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMATTER)));
        MODULE.addSerializer(LocalTime.class, new CustomLocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMATTER)));
        MODULE.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER)));
        MODULE.addSerializer(ZonedDateTime.class, new CustomZonedDateTimeSerializer(DateTimeFormatter.ofPattern(ZONED_DATE_TIME_FORMATTER)));
        MODULE.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer(DateTimeFormatter.ofPattern(OFFSET_DATE_TIME_FORMATTER)));

        MODULE.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMATTER)));
        MODULE.addDeserializer(LocalTime.class, new CustomLocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMATTER)));
        MODULE.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMATTER)));
        MODULE.addDeserializer(ZonedDateTime.class, new CustomZonedDateTimeDeserializer(DateTimeFormatter.ofPattern(ZONED_DATE_TIME_FORMATTER)));
        MODULE.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer(DateTimeFormatter.ofPattern(OFFSET_DATE_TIME_FORMATTER)));
    }

    // start LocalDate

    private static class CustomLocalDateSerializer extends JsonSerializer<LocalDate> implements ContextualSerializer {
        private final DateTimeFormatter formatter;

        private CustomLocalDateSerializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public void serialize(LocalDate date, JsonGenerator generator, SerializerProvider provider) throws IOException {
            var str = date.format(this.formatter);
            generator.writeString(str);
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) {
            return this;
        }
    }

    private static class CustomLocalDateDeserializer extends JsonDeserializer<LocalDate> implements ContextualDeserializer {
        private final DateTimeFormatter formatter;

        private CustomLocalDateDeserializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            var string = jsonParser.getText().trim();
            if (string.isEmpty()) {
                return null;
            }
            return LocalDate.parse(string, this.formatter);
        }

        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
            return this;
        }
    }

    // end LocalDate

    // start LocalTime

    private static class CustomLocalTimeSerializer extends JsonSerializer<LocalTime> implements ContextualSerializer {
        private final DateTimeFormatter formatter;

        private CustomLocalTimeSerializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public void serialize(LocalTime time, JsonGenerator generator, SerializerProvider provider) throws IOException {
            var str = time.format(this.formatter);
            generator.writeString(str);
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) {
            return this;
        }
    }

    private static class CustomLocalTimeDeserializer extends JsonDeserializer<LocalTime> implements ContextualDeserializer {
        private final DateTimeFormatter formatter;

        private CustomLocalTimeDeserializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public LocalTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            var string = jsonParser.getText().trim();
            if (string.isEmpty()) {
                return null;
            }
            return LocalTime.parse(string, this.formatter);
        }

        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
            return this;
        }
    }

    // end LocalTime

    // start LocalDateTime

    private static class CustomLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> implements ContextualSerializer {
        private final DateTimeFormatter formatter;

        private CustomLocalDateTimeSerializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public void serialize(LocalDateTime dateTime, JsonGenerator generator, SerializerProvider provider) throws IOException {
            var str = dateTime.format(this.formatter);
            generator.writeString(str);
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) {
            return this;
        }
    }

    private static class CustomLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> implements ContextualDeserializer {
        private final DateTimeFormatter formatter;

        private CustomLocalDateTimeDeserializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            var string = jsonParser.getText().trim();
            if (string.isEmpty()) {
                return null;
            }
            return LocalDateTime.parse(string, this.formatter);
        }

        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
            return this;
        }
    }

    // end LocalDateTime

    // start ZonedDateTime

    private static class CustomZonedDateTimeSerializer extends JsonSerializer<ZonedDateTime> implements ContextualSerializer {
        private final DateTimeFormatter formatter;

        private CustomZonedDateTimeSerializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public void serialize(ZonedDateTime dateTime, JsonGenerator generator, SerializerProvider provider) throws IOException {
            var str = dateTime.format(this.formatter);
            generator.writeString(str);
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) {
            return this;
        }
    }

    private static class CustomZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> implements ContextualDeserializer {
        private final DateTimeFormatter formatter;

        private CustomZonedDateTimeDeserializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public ZonedDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            var string = jsonParser.getText().trim();
            if (string.isEmpty()) {
                return null;
            }
            return ZonedDateTime.parse(string, this.formatter);
        }

        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
            return this;
        }
    }

    // end ZonedDateTime

    // start OffsetDateTime

    private static class CustomOffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime> implements ContextualSerializer {
        private final DateTimeFormatter formatter;

        private CustomOffsetDateTimeSerializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public void serialize(OffsetDateTime dateTime, JsonGenerator generator, SerializerProvider provider) throws IOException {
            var str = dateTime.format(this.formatter);
            generator.writeString(str);
        }

        @Override
        public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) {
            return this;
        }
    }

    private static class CustomOffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> implements ContextualDeserializer {
        private final DateTimeFormatter formatter;

        private CustomOffsetDateTimeDeserializer(DateTimeFormatter formatter) {
            this.formatter = formatter;
        }

        @Override
        public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            var string = jsonParser.getText().trim();
            if (string.isEmpty()) {
                return null;
            }
            return OffsetDateTime.parse(string, this.formatter);
        }

        @Override
        public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) {
            return this;
        }
    }

    // end OffsetDateTime

}

